package cc.rengu.igas.mcps.core.packswitch;


import cc.rengu.igas.channel.cups.common.enums.RespCodeEnum;
import cc.rengu.igas.mcps.common.constant.McpsParamConstant;
import cc.rengu.igas.mcps.common.constant.McpsTreeNodeConstant;
import cc.rengu.igas.mcps.common.constant.McpsTxnNumConstant;
import cc.rengu.igas.mcps.common.enums.KeyTypeEnum;
import cc.rengu.jradp.packetswitch.IncomingPackSwitchImpl;
import cc.rengu.jradp.packetswitch.OutgoingPackSwitchImpl;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.dao.UserOrderInfoMapper;
import cc.rengu.oltp.service.common.dao.impl.UserOrderInfoMapperImpl;
import cc.rengu.oltp.service.common.entity.SysParam;
import cc.rengu.oltp.service.common.entity.UserOrderInfo;
import cc.rengu.oltp.service.common.enums.ChannelEnum;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.model.TxnInfo;
import cc.rengu.oltp.service.realize.SysParamService;
import cc.rengu.oltp.service.realize.TxnInfoService;
import cc.rengu.oltp.service.realize.impl.SysParamServiceImpl;
import cc.rengu.oltp.service.realize.impl.TxnInfoServiceImpl;
import cc.rengu.oltp.utility.util.*;
import cc.rengu.utility.log.RgLog;
import cc.rengu.utility.log.RgLogger;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;

/**
 * 与pos统一接入服务器通讯层报文解包前后、组前包后实现类
 */
public class PosUnifiedServerPackSwitch implements IncomingPackSwitchImpl, OutgoingPackSwitchImpl {

    @Override
    public byte[] beforeInComPackSwitch(byte[] bytes) throws Exception {
        XmlTreeUtil tree = new XmlTreeUtil();
        String jsonStr = new String(bytes);
        String signBlockString = generateSignBlockString(jsonStr);
        tree.setXmlTreeStringValue(McpsTreeNodeConstant.SIGN_BLOCK, signBlockString);
        return bytes;
    }

    @Override
    public int afterInComPackSwitch() throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        /*设置路由交易所需要的常量*/
        /*设置消息源渠道ID， 智能POS通道default值为 NPOS ；智能终端ILLT*/
        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_SRC_SYS_ID, ChannelEnum.MCPS.getChannelType());
        xmlTreeUtil.copyStringValue(TreeNodeConstant.HEADER_TRACE_NO, TreeNodeConstant.TRACE_NO);
        /*渠道标识设置*/
        String chanelId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.HEADER_CHANNEL_ID);
        if (StringUtil.isEmptyOrNull(chanelId)) {
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_CHANNEL_ID, McpsParamConstant.REQ_NPOS_ID);
        }

        /*
         * 以下为目前所有金额字段，其都为Amt结尾，都不允许为负数，但是有的允许为0
         *  FLD020=commonParam/noPinAmt                                               :commonParam/noPinAmt                                               #免密金额
         *  FLD022=commonParam/noSignAmt                                              :commonParam/noSignAmt                                              #免签金额
         *  FLD034=commonParamList[$n]/noPinAmt                                       :commonParamList[$n]/noPinAmt                                       #免密金额
         *  FLD036=commonParamList[$n]/noSignAmt                                      :commonParamList[$n]/noSignAmt                                      #免签金额
         *  FLD050=couponInfo/discountAmt                                             :couponInfo/discountAmt                                             #优惠券面额
         *  FLD096=orderAmt                                                           :orderAmt                                                           #交易金额
         *  FLD101=orderInfo/discountAmt                                              :orderInfo/discountAmt                                              #订单优惠金额
         *  FLD105=orderInfo/orderAmt                                                 :orderInfo/orderAmt                                                 #交易金额
         *  FLD114=orderInfo/payTypeInfo/tranAmt                                      :orderInfo/payTypeInfo/tranAmt                                      #交易金额
         *  FLD115=orderInfo/realPayAmt                                               :orderInfo/realPayAmt                                               #实际支付金额
         *  FLD116=orderInfo/refundAmt                                                :orderInfo/refundAmt                                                #已退款金额
         *  FLD141=payOrderInfo/orderAmt                                              :payOrderInfo/orderAmt                                              #订单金额
         *  FLD194=stageLimitAmt                                                      :stageLimitAmt                                                      #分期最低限额
         *  FLD198=termCollectDataList[$n]/creditTotalAmt                             :termCollectDataList[$n]/creditTotalAmt                             #贷记总金额
         *  FLD200=termCollectDataList[$n]/debitTotalAmt                              :termCollectDataList[$n]/debitTotalAmt                              #借记总金额
         *  FLD229=transAmt                                                           :transAmt                                                           #金额
         *  FLD236=transInfoList[$n]/transAmt                                         :transInfoList[$n]/transAmt                                         #交易金额
         */
        String discountAmt = xmlTreeUtil.getXmlTreeStringValue("couponInfo/discountAmt");
        if (!StringUtil.isEmptyOrNull(discountAmt)) {
            //优惠券面额 一定大于0
            if (AmountUtil.compare(discountAmt, "0") <= 0) {
                rglog.error("优惠券面额字段非法，{}", discountAmt);
                throw new BizException(cc.rengu.igas.mcps.common.enums.RespCodeEnum.BIZ_PARAM_CHECK_ERROR.getRespCode(),
                        cc.rengu.igas.mcps.common.enums.RespCodeEnum.BIZ_PARAM_CHECK_ERROR.getRespDesc());
            }
        }
        String orderAmt = xmlTreeUtil.getXmlTreeStringValue("orderAmt");
        if (!StringUtil.isEmptyOrNull(orderAmt)) {
            //交易金额有可能为0
            if (AmountUtil.compare(orderAmt, "0") < 0) {
                rglog.error("订单金额非法，{}", orderAmt);
                throw new BizException(cc.rengu.igas.mcps.common.enums.RespCodeEnum.BIZ_PARAM_CHECK_ERROR.getRespCode(),
                        cc.rengu.igas.mcps.common.enums.RespCodeEnum.BIZ_PARAM_CHECK_ERROR.getRespDesc());
            }
        }
        String orderInfoDiscountAmt = xmlTreeUtil.getXmlTreeStringValue("orderInfo/discountAmt");
        if (!StringUtil.isEmptyOrNull(orderInfoDiscountAmt)) {
            //订单优惠金额有可能为0
            if (AmountUtil.compare(orderInfoDiscountAmt, "0") < 0) {
                rglog.error("订单优惠金额非法，{}", orderInfoDiscountAmt);
                throw new BizException(cc.rengu.igas.mcps.common.enums.RespCodeEnum.BIZ_PARAM_CHECK_ERROR.getRespCode(),
                        cc.rengu.igas.mcps.common.enums.RespCodeEnum.BIZ_PARAM_CHECK_ERROR.getRespDesc());
            }
        }
        String orderInfoOrderAmt = xmlTreeUtil.getXmlTreeStringValue("orderInfo/orderAmt");
        if (!StringUtil.isEmptyOrNull(orderInfoOrderAmt)) {
            //订单金额有可能为0
            if (AmountUtil.compare(orderInfoOrderAmt, "0") < 0) {
                rglog.error("订单金额非法，{}", orderInfoOrderAmt);
                throw new BizException(cc.rengu.igas.mcps.common.enums.RespCodeEnum.BIZ_PARAM_CHECK_ERROR.getRespCode(),
                        cc.rengu.igas.mcps.common.enums.RespCodeEnum.BIZ_PARAM_CHECK_ERROR.getRespDesc());
            }
        }
        String orderInfoRealPayAmt = xmlTreeUtil.getXmlTreeStringValue("orderInfo/realPayAmt");
        if (!StringUtil.isEmptyOrNull(orderInfoRealPayAmt)) {
            //订单实际支付金额有可能为0
            if (AmountUtil.compare(orderInfoRealPayAmt, "0") < 0) {
                rglog.error("订单实际支付金额非法，{}", orderInfoRealPayAmt);
                throw new BizException(cc.rengu.igas.mcps.common.enums.RespCodeEnum.BIZ_PARAM_CHECK_ERROR.getRespCode(),
                        cc.rengu.igas.mcps.common.enums.RespCodeEnum.BIZ_PARAM_CHECK_ERROR.getRespDesc());
            }
        }
        String orderInfoRefundAmt = xmlTreeUtil.getXmlTreeStringValue("orderInfo/refundAmt");
        if (!StringUtil.isEmptyOrNull(orderInfoRefundAmt)) {
            //订单退款金额有大于0
            if (AmountUtil.compare(orderInfoRefundAmt, "0") <= 0) {
                rglog.error("订单退款金额非法，{}", orderInfoRefundAmt);
                throw new BizException(cc.rengu.igas.mcps.common.enums.RespCodeEnum.BIZ_PARAM_CHECK_ERROR.getRespCode(),
                        cc.rengu.igas.mcps.common.enums.RespCodeEnum.BIZ_PARAM_CHECK_ERROR.getRespDesc());
            }
        }
        String orderInfoPayTypeInfoOrderAmt = xmlTreeUtil.getXmlTreeStringValue("orderInfo/payTypeInfo/tranAmt");
        if (!StringUtil.isEmptyOrNull(orderInfoPayTypeInfoOrderAmt)) {
            //订单金额有大于等于0
            if (AmountUtil.compare(orderInfoPayTypeInfoOrderAmt, "0") < 0) {
                rglog.error("订单金额非法，{}", orderInfoPayTypeInfoOrderAmt);
                throw new BizException(cc.rengu.igas.mcps.common.enums.RespCodeEnum.BIZ_PARAM_CHECK_ERROR.getRespCode(),
                        cc.rengu.igas.mcps.common.enums.RespCodeEnum.BIZ_PARAM_CHECK_ERROR.getRespDesc());
            }
        }
        String payOrderInfoOrderAmt = xmlTreeUtil.getXmlTreeStringValue("payOrderInfo/orderAmt");
        if (!StringUtil.isEmptyOrNull(payOrderInfoOrderAmt)) {
            //订单金额有大于等于0
            if (AmountUtil.compare(payOrderInfoOrderAmt, "0") < 0) {
                rglog.error("订单金额非法，{}", payOrderInfoOrderAmt);
                throw new BizException(cc.rengu.igas.mcps.common.enums.RespCodeEnum.BIZ_PARAM_CHECK_ERROR.getRespCode(),
                        cc.rengu.igas.mcps.common.enums.RespCodeEnum.BIZ_PARAM_CHECK_ERROR.getRespDesc());
            }
        }
        String stageLimitAmt = xmlTreeUtil.getXmlTreeStringValue("stageLimitAmt");
        if (!StringUtil.isEmptyOrNull(stageLimitAmt)) {
            //分期金额有大于0
            if (AmountUtil.compare(stageLimitAmt, "0") <= 0) {
                rglog.error("分期金额非法，{}", stageLimitAmt);
                throw new BizException(cc.rengu.igas.mcps.common.enums.RespCodeEnum.BIZ_PARAM_CHECK_ERROR.getRespCode(),
                        cc.rengu.igas.mcps.common.enums.RespCodeEnum.BIZ_PARAM_CHECK_ERROR.getRespDesc());
            }
        }
        String transAmt = xmlTreeUtil.getXmlTreeStringValue("transAmt");
        if (!StringUtil.isEmptyOrNull(transAmt)) {
            //分期金额有大于0
            if (AmountUtil.compare(transAmt, "0") <= 0) {
                rglog.error("交易金额非法，{}", transAmt);
                throw new BizException(cc.rengu.igas.mcps.common.enums.RespCodeEnum.BIZ_PARAM_CHECK_ERROR.getRespCode(),
                        cc.rengu.igas.mcps.common.enums.RespCodeEnum.BIZ_PARAM_CHECK_ERROR.getRespDesc());
            }
        }

        /*设置处理交易时间*/
        String transDate = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.HEADER_TRANS_DATE);
        String transTime = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.HEADER_TRANS_TIME);
        String transDateTime = xmlTreeUtil.getXmlTreeStringValue("orderTime");
        if (!StringUtil.isEmptyOrNull(transDateTime)) {
            if (transDateTime.length() >= 14) {
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_TRANS_DATE, transDateTime.substring(0, 8));
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_TRANS_TIME, transDateTime.substring(8));
            } else {
                if (StringUtil.isEmptyOrNull(transDate)) {
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_TRANS_DATE, DateUtil.getCurrentDate());
                }
                if (StringUtil.isEmptyOrNull(transTime)) {
                    xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_TRANS_TIME, DateUtil.getCurrentTime());
                }
            }
        } else {
            if (StringUtil.isEmptyOrNull(transDate)) {
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_TRANS_DATE, DateUtil.getCurrentDate());
            }
            if (StringUtil.isEmptyOrNull(transTime)) {
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_TRANS_TIME, DateUtil.getCurrentTime());
            }
        }
        /*设置报文头版本号*/
        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.VERSION, "1.0.0");
        /*设置业务分组 00-默认；01-微信；02-支付宝；03-银联二维码  需在系统参数表进行配置*/
        if (!xmlTreeUtil.isExist(TreeNodeConstant.BIZ_TYPE)) {
            if (xmlTreeUtil.isExist(McpsTreeNodeConstant.QRAUTHCODE)) {
                /*区分微信、支付宝、银联二维码交易，进行业务细分*/
                String bizParamType = McpsParamConstant.NORMAL_BIZ_TYPE_PREFIX;
                String bizParamKey = xmlTreeUtil.getXmlTreeStringValue(McpsTreeNodeConstant.QRAUTHCODE).substring(0, 2);
                SysParamService sysParamService = new SysParamServiceImpl();
                SysParam sysParam = sysParamService.getSysParamInfo("000", bizParamType, bizParamKey);
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.BIZ_TYPE, sysParam.getParamValue());
            } else {
                xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.BIZ_TYPE, AppParamConstant.DEFAULT_BIZ_TYPE);
            }
        }
        /*设置业务分组 00-默认；01-微信；02-支付宝；03-银联二维码  需在系统参数表进行配置*/
        if (McpsTxnNumConstant.NPOS_QR_CONSUME_REFUND_TXN_NUM.equals(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.HEADER_TXN_NUM))) {
            String instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.HEADER_INST_ID);
            String msgSrcId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.HEADER_SRC_SYS_ID);
            String transChannelId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.HEADER_CHANNEL_ID);
            String oriTraceNo = xmlTreeUtil.getXmlTreeStringValue("oriTraceNo");
            /*原商户订单号*/
            String oriPayId = xmlTreeUtil.getXmlTreeStringValue("oriPayId");

            /*查询用户订单表，商户订单信息，支付订单信息表*/
            if (null == oriTraceNo && null == oriPayId) {
                rglog.error("交易报文未上送商户订单号或者流水号");
                throw new BizException(cc.rengu.igas.mcps.common.enums.RespCodeEnum.POS_FORMAT_ERROR.getRespCode(), cc.rengu.igas.mcps.common.enums.RespCodeEnum.POS_FORMAT_ERROR.getRespDesc());
            }
            /*订单日期（如果在日切点，可能会造成，输入当天日期，可能查不到用户订单信息，日期要递减一天）*/
            UserOrderInfoMapper userOrderInfoMapper = new UserOrderInfoMapperImpl();
            UserOrderInfo oriUserOrderInfo;
            if (oriTraceNo != null) {
                oriUserOrderInfo = userOrderInfoMapper.selectUserOrderInfoByFrontSeqNum(instId, oriTraceNo, msgSrcId, transChannelId);
                if (null == oriUserOrderInfo) {
                    /*如果还没查到用户订单表信息，返回拒绝交易*/
                    oriUserOrderInfo = userOrderInfoMapper.selectUserOrderInfoByFrontSeqNum(instId, oriTraceNo, msgSrcId, "*");
                    if (null == oriUserOrderInfo) {
                        rglog.error("原交易流水不存在");
                        throw new BizException(cc.rengu.igas.mcps.common.enums.RespCodeEnum.POS_ORIG_TXN_NOT_FOUND.getRespCode(), cc.rengu.igas.mcps.common.enums.RespCodeEnum.POS_ORIG_TXN_NOT_FOUND.getRespDesc());
                    }
                }
            } else {
                /*原用户流水号,发起查证的系统和下单的系统同一系统*/
                oriUserOrderInfo = userOrderInfoMapper.selectUserOrderInfoByUserOrderId(instId, oriPayId);
                if (null == oriUserOrderInfo) {
                    rglog.error("原交易流水不存在");
                    throw new BizException(cc.rengu.igas.mcps.common.enums.RespCodeEnum.POS_ORIG_TXN_NOT_FOUND.getRespCode(), cc.rengu.igas.mcps.common.enums.RespCodeEnum.POS_ORIG_TXN_NOT_FOUND.getRespDesc());
                }
            }
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.BIZ_TYPE, oriUserOrderInfo.getBizType());
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.HEADER_BIZ_TYPE, oriUserOrderInfo.getBizType());
        }

        return 0;
    }

    @Override
    public int beforeOutgoPackSwitch() throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String txnNum = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.HEADER_TXN_NUM);
        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.TXN_NUM, txnNum);
        /*设置通讯层返回报文*/
        xmlTreeUtil.setXmlTreeStringValue(McpsTreeNodeConstant.RESP_ERROR, "000000");
        xmlTreeUtil.setXmlTreeStringValue(McpsTreeNodeConstant.RESP_ERROR_DESCRIPTION, xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.RESP_DESC));
        return 0;
    }

    @Override
    public byte[] afterOutgoPackSwitch(byte[] bytes) throws Exception {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());

        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        TxnInfo txnInfo = (TxnInfo) xmlTreeUtil.getXmlTreeObjectValue(TreeNodeConstant.TXN_INFO, TxnInfo.class);
        if(null != txnInfo && AppParamConstant.YES.equals(txnInfo.getSrcTxnAuthCfg().getSignFlag())){
            String jsonBefore = new String(bytes);
            String signBlock = generateSignBlockString(jsonBefore);
            /* 生成生成数字签名的字符串 */
            if (null == signBlock) {
                rglog.error("生成加签Block失败!");
                throw new BizException(RespCodeEnum.GENERATE_SIGN_BLOCK_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_BLOCK_ERROR.getRespDesc());
            }
            rglog.debug("加签Block:<{}>", signBlock);
            String macKey = xmlTreeUtil.getXmlTreeStringValue(KeyTypeEnum.TAK.name());
            String signData = MD5Util.generateSignString(signBlock, "&key=", macKey);
            if (null == signData) {
                rglog.error("生成数字签名失败!");
                throw new BizException(RespCodeEnum.GENERATE_SIGN_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_ERROR.getRespDesc());
            }
            rglog.debug("数字签名:<{}>", signData);

            JSONObject jsonObj = JSON.parseObject(jsonBefore, Feature.OrderedField);
            JSONObject headObj = jsonObj.getJSONObject(McpsTreeNodeConstant.HEADER);
            headObj.put(McpsTreeNodeConstant.SIGNATURE, signData);
            jsonObj.put(McpsTreeNodeConstant.HEADER, headObj);
            rglog.debug("添加signature后发送报文:<{}>", jsonObj.toJSONString());
            return jsonObj.toJSONString().getBytes();
        } else {
            return bytes;
        }
    }

    /**
     * 生成加签Block
     *
     * @param jsonString 原始报文
     * @return 加签BLOCK
     */
    private String generateSignBlockString(String jsonString) {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());

        try {
            if (null != jsonString && 0 != jsonString.trim().length()) {
                rglog.debug("jsonString=[{}]", jsonString);
                JSONObject jsonObject = JSONObject.parseObject(jsonString, Feature.OrderedField);
                Map<String, Object> maps = jsonObject.getInnerMap();
                ArrayList<String> arrayList = new ArrayList<>();
                int i = 0;
                Object key;
                Object value;
                for (Map.Entry<String, Object> entry : maps.entrySet()) {
                    key = entry.getKey();
                    value = entry.getValue();
                    if (!McpsTreeNodeConstant.HEADER.equals(key) && null != value) {
                        arrayList.add(key + "=" + value + "&");
                    } else if (McpsTreeNodeConstant.HEADER.equals(key) && null != value) {
                        JSONObject headObj = JSONObject.parseObject(value.toString(), Feature.OrderedField);
                        headObj.remove(McpsTreeNodeConstant.SIGNATURE);
                        arrayList.add(key + "=" + headObj + "&");
                    }
                    i++;
                }
                String[] strArray = arrayList.toArray(new String[0]);
                Arrays.sort(strArray);
                StringBuilder stringBuffer = new StringBuilder();
                for (i = 0; i < strArray.length; i++) {
                    stringBuffer.append(strArray[i]);
                }
                return stringBuffer.substring(0, stringBuffer.length() - 1);
            }
        } catch (Exception e) {
            rglog.error("系统异常!");
            return null;
        }
        return null;
    }
}
